<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      class Emitter {
        // 存储事件名和回调函数的容器
        // key -> 事件名  value -> [回调函数, 回调函数...]
        events = {};
        // 绑定事件
        // subscribe -> 订阅消息
        /**
         * 绑定事件
         * @params type 事件类型
         * @params callback 事件回调函数
         */
        on(type, callback) {
          // 将事件名和回调函数存储到容器中
          const callbacks = this.events[type];
          if (callbacks) {
            // 第二次有值，追加回调函数
            callbacks.push(callback);
          } else {
            // 第一次没有值，需要赋值为数组
            this.events[type] = [callback];
          }
        }

        // 解绑事件
        // unsubscribe -> 取消订阅
        /**
         * 解绑事件
         * @params type 事件类型
         * @params callback 事件回调函数
         */
        off(type, callback) {
          const callbacks = this.events[type];
          if (!callbacks) return;

          if (callback) {
            // 解绑某个事件的一个回调函数
            // 另外一种写法：callbacks.splice(callbacks.indexOf(callback) >>> 0, 1)
            this.events[type] = callbacks.filter((cb) => cb !== callback);
          } else {
            // 解绑某个事件的所有回调函数
            delete this.events[type];
          }
        }

        // 触发事件
        // publish -> 发布消息
        /**
         * 触发事件
         * @params type 事件类型
         * @params data 参数
         */
        emit(type, data) {
          const callbacks = this.events[type];
          callbacks && callbacks.forEach((cb) => cb(data));
        }
      }

      const myEmitter = new Emitter();

      // 测试1
      // myEmitter.on("event1", (data) => {
      //   console.log("回调1", data);
      // });
      // myEmitter.on("event2", (data) => {
      //   console.log("回调2", data);
      // });
      // myEmitter.on("event1", (data) => {
      //   console.log("回调3", data);
      // });

      // myEmitter.emit("event1", 111);
      // 浏览器要打印：111 111 111

      // 测试2
      const cb1 = () => {
        console.log("111");
      };
      const cb2 = () => {
        console.log("222");
      };
      const cb3 = () => {
        console.log("333");
      };
      myEmitter.on("event1", cb1);
      myEmitter.on("event1", cb2);
      myEmitter.on("event1", cb3);

      myEmitter.emit("event1"); // 111 222 333
      myEmitter.off("event1", cb2);
      myEmitter.emit("event1"); // 111 333
      myEmitter.off("event1");
      myEmitter.emit("event1");
    </script>
  </body>
</html>
